package promentheus

import (
	"context"
	"encoding/json"
	"github.com/google/wire"
	"github.com/pkg/errors"
	"github.com/prometheus/client_golang/api"
	v1 "github.com/prometheus/client_golang/api/prometheus/v1"
	"github.com/prometheus/common/model"
	"github.com/spf13/viper"
	"go.uber.org/zap"
	"io/ioutil"
	"net/http"
	"strings"
	"time"
)

type PrometheusService struct {
	logger   *zap.Logger
	v        *viper.Viper
	alertUrl string
	result   *AlertResult
}

func NewPrometheusService(logger *zap.Logger, v *viper.Viper) *PrometheusService {
	return &PrometheusService{
		logger:   logger,
		v:        v,
		alertUrl: v.GetString("prometheus.alert"),
	}
}

func (p *PrometheusService) GetAlerts() (*AlertResult, error) {
	if p.result == nil {
		err := p.Reload()
		if err != nil {
			return nil, err
		}
	}
	return p.result, nil
}

func (p *PrometheusService) Reload() error {
	resp, err := http.Get(p.alertUrl)
	if err != nil {
		return errors.Wrap(err, "http get prometheus api alert error")
	}
	defer resp.Body.Close()

	alertResult := new(AlertResult)

	contents, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return errors.Wrap(err, "read prometheus alert error")
	}
	err = json.Unmarshal(contents, &alertResult)
	if err != nil {
		return errors.Wrap(err, "Unmarshal alertinfo error")
	}

	if alertResult.Status != "success" {
		return errors.Wrap(err, "alert result status != success")
	}

	p.result = alertResult
	return nil
}

//region 数据查询
//通过api查询时间范围内的样点
func (p *PrometheusService) QueryRangeProm(expression string, date *Date) (model.Value, error) {
	ctx := context.Background()
	client, err := api.NewClient(api.Config{
		Address: p.v.GetString("prometheus.url"),
	})
	if err != nil {
		p.logger.Error("prometheus query init httpClient error:", zap.Error(err))
		return nil, err
	}
	queryApi := v1.NewAPI(client)
	result, _, err := queryApi.QueryRange(ctx, expression, v1.Range{Start: date.Start, End: date.End, Step: date.Step})
	if err != nil {
		p.logger.Error("prometheus query error:", zap.Error(err))
		return nil, errors.Wrap(err, "prometheus query error")
	}
	return result, nil
}

//通过表达式查询prometheus获取指标的数值
//查询结果是单个值，忽略返回的表达式
//查不到结果返回0
func (p *PrometheusService) QueryVector(express string, now time.Time) float64 {
	ctx := context.Background()
	client, err := api.NewClient(api.Config{
		Address: p.v.GetString("prometheus.url"),
	})
	if err != nil {
		p.logger.Error("prometheus new client err:", zap.Error(err))
		return 0
	}
	queryApi := v1.NewAPI(client)
	result, _, err := queryApi.Query(ctx, express, now)
	if err != nil {
		p.logger.Error("prometheus query err:", zap.Error(err))
		return 0
	}
	switch result.(type) {
	case model.Vector:
		{
			vector, ok := result.(model.Vector)
			if ok && len(vector) > 0 {
				return float64(vector[0].Value)
			}
		}
	default:
	}
	return 0
}

func (p *PrometheusService) QueryVectorProm(express string, now time.Time) (model.Value, error) {
	ctx := context.Background()
	client, err := api.NewClient(api.Config{
		Address: p.v.GetString("prometheus.url"),
	})
	if err != nil {
		p.logger.Error("prometheus new client err:", zap.Error(err))
		return nil, err
	}
	queryApi := v1.NewAPI(client)
	result, _, err := queryApi.Query(ctx, express, now)
	if err != nil {
		p.logger.Error("prometheus query err:", zap.Error(err))
		return nil, err
	}

	return result, nil
}

func (p *PrometheusService) QueryUpstream(method, service string, now time.Time) ([]string, error) {
	ctx := context.Background()
	client, err := api.NewClient(api.Config{
		Address: p.v.GetString("prometheus.url"),
	})
	express := viper.GetString("app.upstream")
	express = strings.ReplaceAll(express, "{{.method}}", method)
	express = strings.ReplaceAll(express, "{{.service}}", service)
	if err != nil {
		p.logger.Error("prometheus new client err:", zap.Error(err))
		return nil, err
	}
	queryApi := v1.NewAPI(client)
	result, _, err := queryApi.Query(ctx, express, now)
	if err != nil {
		p.logger.Error("prometheus query err:", zap.Error(err))
		return nil, err
	}
	appNames := make([]string, 0)
	switch result.(type) {
	case model.Vector:
		{
			vector, ok := result.(model.Vector)
			if ok {
				for _, v := range vector {
					appNames = append(appNames, string(v.Metric["app"]))

				}
			}
		}
	default:
	}
	return appNames, nil
}

//endregion

var ProviderSet = wire.NewSet(NewPrometheusService)
